Skip to main content

6.1 - Macro-management - Expanding and Managing Your Economy

Macro-management ("macro") is the art of economy. It is a high-level, strategic process focused on resource collection, base expansion, and infrastructure development. A bot with superior macro can often defeat a bot with superior unit control ("micro") simply by producing a larger, better-equipped army.

This section moves beyond individual commands and into the realm of building a robust economic engine that will power your bot's war machine.

The Macro Engine: A System of Priorities

A strong macro bot doesn't just perform actions; it manages a system of competing priorities. At any given moment, it must decide what is the most important use of its resources.

+---------------------------------+
| Incoming Resources | (Minerals & Vespene)
| (self.minerals, self.vespene) |
+---------------------------------+
|
v
+---------------------------------+
| The Macro Engine (Your Bot) |
|---------------------------------|
| #1: Manage Supply | <-- Top Priority: Never get supply blocked.
| #2: Produce Workers | <-- The foundation of your economy.
| #3: Expand to New Bases | <-- Increases your income ceiling.
| #4: Build Production/Tech | <-- Unlocks new units and upgrades.
| #5: Build Army Units | <-- Your win condition.
+---------------------------------+

Your on_step method should be structured to reflect this hierarchy, ensuring that critical economic tasks are always handled before less urgent ones.


The Three Pillars of Economic Management

PillarCore PrincipleImplementation Checklist
1. Resource VelocityKeep your money moving. A large bank of unspent resources is a sign of inefficiency. Your goal is to have a high rate of spending.[ ] Is my production always queued?
[ ] Am I building tech as soon as I can afford it?
[ ] Are my resources consistently low?
2. Worker SaturationBuild workers constantly, and assign them correctly. The goal is to "saturate" each base with the optimal number of workers.[ ] Am I training workers from all idle townhalls?
[ ] Do I stop at a target worker count (e.g., 66-70)?
[ ] Are my vespene geysers fully staffed (3 workers each)?
3. Base ExpansionSecure more income. To build a larger army, you must expand to new mineral lines.[ ] Do I have a clear trigger for when to expand? (e.g., at a certain time, supply, or tech level).
[ ] Am I using self.expand_now() for easy, reliable expansion?

Code Architecture: Separating Concerns

As your bot's logic grows, a single, massive on_step method becomes unmanageable. A professional approach is to separate concerns into different classes or methods. Here, we'll create a MacroManager to handle all economic decisions.

Code Example: The "Economic Engine" Bot

This bot demonstrates a clean, structured approach to macro. It uses a dedicated MacroManager class to handle its economy, following a strict priority list to ensure it grows efficiently and robustly.

# economic_engine_bot.py

from sc2 import maps
from sc2.bot_ai import BotAI
from sc2.data import Difficulty, Race
from sc2.main import run_game
from sc2.player import Bot, Computer
from sc2.ids.unit_typeid import UnitTypeId

# A class dedicated to handling all macroeconomic decisions.
class MacroManager:
def __init__(self, bot: BotAI):
self.bot = bot
self.target_worker_count = 70

async def manage(self):
# The order of these calls defines our bot's priorities.
self.manage_supply()
self.manage_worker_production()
await self.manage_gas_collection()
await self.manage_expansion()
self.manage_production_buildings()
self.manage_army_production()

def manage_supply(self):
"""Builds supply depots when needed."""
if self.bot.supply_left < 5 and self.bot.already_pending(UnitTypeId.SUPPLYDEPOT) == 0:
if self.bot.can_afford(UnitTypeId.SUPPLYDEPOT):
worker = self.bot.workers.random_or_none
if worker:
worker.build(UnitTypeId.SUPPLYDEPOT, near=self.bot.start_location.towards(self.bot.game_info.map_center, 5))

def manage_worker_production(self):
"""Trains workers from idle townhalls."""
if self.bot.workers.amount < self.target_worker_count:
for townhall in self.bot.townhalls.idle:
if self.bot.can_afford(UnitTypeId.SCV):
townhall.train(UnitTypeId.SCV)

async def manage_gas_collection(self):
"""Builds refineries and saturates them with workers."""
# Build refineries
for townhall in self.bot.townhalls.ready:
vespene_geysers = self.bot.vespene_geysers.closer_than(10, townhall)
for geyser in vespene_geysers:
if not self.bot.structures.closer_than(1.0, geyser).exists and self.bot.can_afford(UnitTypeId.REFINERY):
worker = self.bot.workers.closest_to(geyser)
if worker:
worker.build(UnitTypeId.REFINERY, geyser)
break # Prevents assigning multiple workers to build the same refinery
# Saturate refineries
for refinery in self.bot.structures(UnitTypeId.REFINERY).ready:
if refinery.assigned_harvesters < refinery.ideal_harvesters:
worker = self.bot.workers.closest_to(refinery)
if worker:
worker.gather(refinery)

async def manage_expansion(self):
"""Expands to a new base when conditions are met."""
if self.bot.townhalls.amount < 3 and self.bot.can_afford(UnitTypeId.COMMANDCENTER):
await self.bot.expand_now()

def manage_production_buildings(self):
"""Builds barracks to produce army units."""
if self.bot.structures(UnitTypeId.SUPPLYDEPOT).ready.exists and self.bot.structures(UnitTypeId.BARRACKS).amount < 5:
if self.bot.can_afford(UnitTypeId.BARRACKS):
worker = self.bot.workers.random_or_none
if worker:
worker.build(UnitTypeId.BARRACKS, near=self.bot.start_location.towards(self.bot.game_info.map_center, 8))

def manage_army_production(self):
"""Trains marines from idle barracks."""
for barracks in self.bot.structures(UnitTypeId.BARRACKS).ready.idle:
if self.bot.can_afford(UnitTypeId.MARINE):
barracks.train(UnitTypeId.MARINE)


# The main bot class now delegates its macro tasks.
class MacroBot(BotAI):
def __init__(self):
self.macro_manager = MacroManager(self)

async def on_step(self, iteration: int):
# On each step, we simply tell our manager to do its job.
await self.macro_manager.manage()


if __name__ == "__main__":
run_game(
maps.get("GresvanAIE"),
[
Bot(Race.Terran, MacroBot()),
Computer(Race.Zerg, Difficulty.Medium)
],
realtime=False,
)